深入比较用于 Python 包管理的 setup.py 和 pyproject.toml,涵盖最佳实践、迁移策略和现代工具。
Python 包结构:setup.py vs. pyproject.toml - 全面指南
多年来,setup.py
文件一直是 Python 包管理的基石。然而,随着技术的发展,pyproject.toml
已成为一种现代化的替代方案。本全面指南将探讨这两种方法之间的差异,帮助您了解哪种方法适合您的项目,以及如何有效地管理您的 Python 包。
了解基础知识
什么是 Python 包?
Python 包是一种组织和分发 Python 代码的方式。它允许您将相关的模块组合成一个目录层次结构,使您的代码更具模块化、可重用性和可维护性。包对于与他人共享代码以及管理项目中的依赖关系至关重要。
包元数据的作用
包元数据提供了关于您的包的基本信息,例如其名称、版本、作者、依赖项和入口点。像 pip
这样的包管理器使用这些元数据来安装、升级和管理您的包。在过去,setup.py
是定义这些元数据的主要方式。
Setup.py:传统方法
什么是 Setup.py?
setup.py
是一个 Python 脚本,它使用 setuptools
库来定义包的结构和元数据。它是一个动态执行的文件,这意味着它通过运行 Python 代码来配置包。
Setup.py 的关键组件
一个典型的 setup.py
文件包含以下组件:
- 包名称: 您的包的名称(例如,
my_package
)。 - 版本: 您的包的版本号(例如,
1.0.0
)。 - 作者和维护者信息: 关于包的作者和维护者的详细信息。
- 依赖项: 您的包所依赖的其他包的列表(例如,
requests >= 2.20.0
)。 - 入口点: 为命令行脚本或包的其他入口点进行的定义。
- 包数据: 应包含在包中的非代码文件(例如,配置文件、数据文件)。
Setup.py 示例
```python from setuptools import setup, find_packages setup( name='my_package', version='1.0.0', author='John Doe', author_email='john.doe@example.com', description='A simple Python package', packages=find_packages(), install_requires=[ 'requests >= 2.20.0', ], entry_points={ 'console_scripts': [ 'my_script = my_package.module:main', ], }, classifiers=[ 'Programming Language :: Python :: 3', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', ], ) ```Setup.py 的优点
- 熟悉度: 这是一种传统且广为人知的方法,因此许多开发人员已经熟悉它。
- 灵活性: 因为它是一个 Python 脚本,所以提供了高度的灵活性。您可以根据需要执行复杂的逻辑并自定义构建过程。
- 可扩展性: Setuptools 提供了一套丰富的功能,并可以通过自定义命令和扩展进行扩展。
Setup.py 的缺点
- 动态执行:
setup.py
的动态特性可能带来安全风险,因为它在构建过程中会执行任意代码。 - 隐式依赖:
setup.py
通常依赖于隐式依赖项,例如 setuptools 本身,这可能导致不一致和错误。 - 复杂性: 对于复杂的项目,
setup.py
可能会变得庞大且难以维护。 - 有限的声明式配置: 大部分包元数据是命令式而非声明式定义的,这使得推理和理解变得更加困难。
Pyproject.toml:现代替代方案
什么是 Pyproject.toml?
pyproject.toml
是一个配置文件,它使用 TOML (Tom's Obvious, Minimal Language) 格式来定义包的构建系统和元数据。这是一种声明式方法,意味着您只需指定想要实现的目标,而无需关心如何实现。
Pyproject.toml 的关键部分
一个典型的 pyproject.toml
文件包含以下几个部分:
[build-system]
: 定义要使用的构建系统(例如,setuptools
、poetry
、flit
)。[project]
: 包含有关项目的元数据,例如其名称、版本、描述、作者和依赖项。[tool.poetry]
或[tool.flit]
: 用于特定工具配置的部分(例如,Poetry、Flit)。
Pyproject.toml 示例 (使用 Setuptools)
```toml [build-system] requires = ["setuptools>=61.0"] build-backend = "setuptools.build_meta" [project] name = "my_package" version = "1.0.0" description = "A simple Python package" authors = [ { name = "John Doe", email = "john.doe@example.com" } ] dependencies = [ "requests >= 2.20.0", ] [project.scripts] my_script = "my_package.module:main" [project.optional-dependencies] dev = [ "pytest", "flake8", ] [project.classifiers] classifiers = [ "Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ] [project.urls] homepage = "https://example.com" repository = "https://github.com/example/my_package" ```Pyproject.toml 示例 (使用 Poetry)
```toml [tool.poetry] name = "my_package" version = "1.0.0" description = "A simple Python package" authors = ["John DoePyproject.toml 的优点
- 声明式配置:
pyproject.toml
提供了一种声明式的方式来定义包元数据,使其更易于理解和维护。 - 标准化的构建系统: 它指定了要使用的构建系统,确保在不同环境中构建的一致性。
- 改进的依赖管理: 像 Poetry 和 Pipenv 这样的工具与
pyproject.toml
无缝集成,提供强大的依赖管理功能。 - 降低安全风险: 因为它是一个静态配置文件,所以消除了在构建过程中动态执行代码相关的安全风险。
- 与现代工具集成:
pyproject.toml
是像 Poetry、Pipenv 和 Flit 等现代 Python 打包工具的标准。
Pyproject.toml 的缺点
- 学习曲线: 开发人员可能需要学习一种新的语法 (TOML) 和一种关于包管理的新的思维方式。
- 灵活性有限: 对于需要复杂逻辑的高度定制化的构建过程,它可能不太适合。
- 工具依赖: 您需要选择并学习如何使用特定的构建系统(例如,Setuptools、Poetry、Flit)。
比较 Setup.py 和 Pyproject.toml
下表总结了 setup.py
和 pyproject.toml
之间的主要区别:
功能 | Setup.py | Pyproject.toml |
---|---|---|
配置风格 | 命令式 (Python 代码) | 声明式 (TOML) |
构建系统 | 隐式 (Setuptools) | 显式 (在 [build-system] 中指定) |
安全性 | 潜在安全性较低 (动态执行) | 更安全 (静态配置) |
依赖管理 | 基础 (install_requires ) |
高级 (与 Poetry, Pipenv 集成) |
工具 | 传统 (Setuptools) | 现代 (Poetry, Pipenv, Flit) |
灵活性 | 高 | 中等 |
复杂性 | 对于复杂项目可能较高 | 通常较低 |
迁移策略:从 Setup.py 到 Pyproject.toml
从 setup.py
迁移到 pyproject.toml
可能看起来令人生畏,但为了长期的可维护性和一致性,这是一项值得的投资。以下是您可以使用的几种策略:
1. 从一个最小化的 Pyproject.toml 开始
创建一个基本的 pyproject.toml
文件来指定构建系统,然后逐步将元数据从 setup.py
迁移到 pyproject.toml
。
2. 将 Setuptools 与 Pyproject.toml 结合使用
继续使用 Setuptools 作为您的构建系统,但在 pyproject.toml
中定义项目元数据。这使您可以在继续使用熟悉工具的同时,利用 pyproject.toml
的优势。
3. 迁移到像 Poetry 这样的现代工具
考虑迁移到像 Poetry 或 Pipenv 这样的现代工具。这些工具提供了全面的依赖管理功能,并与 pyproject.toml
无缝集成。
示例:迁移到 Poetry
- 安装 Poetry:
pip install poetry
- 在您的项目中初始化 Poetry:
poetry init
(这将引导您创建一个pyproject.toml
文件) - 添加您的依赖项:
poetry add requests
(或其他任何依赖项) - 构建您的包:
poetry build
4. 使用工具进行自动化迁移
有些工具可以帮助自动化迁移过程。例如,您可以使用工具将您的 setup.py
文件转换为 pyproject.toml
文件。
Python 包管理最佳实践
1. 使用虚拟环境
始终使用虚拟环境来隔离您项目的依赖项与系统范围的 Python 安装。这可以防止冲突,并确保您的项目拥有正确的依赖项。
使用 venv
的示例:
使用 conda
的示例:
2. 精确指定依赖项
使用版本约束来指定依赖项的兼容版本。这可以防止因库更新不兼容而导致的意外行为。使用像 pip-tools
这样的工具来管理您的依赖项。
依赖项规范示例:
``` requests >= 2.20.0, < 3.0.0 ```3. 使用一致的构建系统
选择一个构建系统(例如,Setuptools、Poetry、Flit)并坚持使用它。这可以确保在不同环境中的构建一致性,并简化打包过程。
4. 为您的包编写文档
为您的包编写清晰简洁的文档。这有助于用户理解如何使用您的包,并使其他人更容易为您的项目做出贡献。使用像 Sphinx 这样的工具从您的代码生成文档。
5. 使用持续集成 (CI)
设置一个 CI 系统(例如,GitHub Actions、Travis CI、GitLab CI)来在代码发生更改时自动构建、测试和部署您的包。这有助于确保您的包始终处于可工作状态。
GitHub Actions 配置示例:
```yaml name: Python 包 on: push: branches: [ main ] pull_request: branches: [ main ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: 设置 Python 3.9 uses: actions/setup-python@v4 with: python-version: 3.9 - name: 安装依赖项 run: | python -m pip install --upgrade pip pip install poetry poetry install - name: 使用 flake8 进行代码检查 run: | poetry run flake8 . - name: 使用 pytest 进行测试 run: | poetry run pytest ```6. 将您的包发布到 PyPI
通过将您的包发布到 Python 包索引 (PyPI) 来与世界分享。这使得其他人可以轻松安装和使用您的包。
发布到 PyPI 的步骤:
- 在 PyPI 和 TestPyPI 上注册一个帐户。
- 安装
twine
:pip install twine
。 - 构建您的包:
poetry build
或python setup.py sdist bdist_wheel
。 - 将您的包上传到 TestPyPI:
twine upload --repository testpypi dist/*
。 - 将您的包上传到 PyPI:
twine upload dist/*
。
实际案例
让我们看看一些流行的 Python 项目是如何使用 pyproject.toml
的:
- Poetry: 使用
pyproject.toml
进行自身的包管理。 - Black: 这个不妥协的代码格式化工具也使用
pyproject.toml
。 - FastAPI: 一个用于构建 API 的现代化、快速(高性能)的 Python Web 框架也使用了它。
结论
pyproject.toml
代表了 Python 包管理的现代标准,提供了一种声明式且安全的方式来定义包元数据和管理依赖项。虽然 setup.py
曾为我们提供了很好的服务,但迁移到 pyproject.toml
是为了长期可维护性、一致性以及与现代工具集成的值得投资。通过采用最佳实践和利用正确的工具,您可以简化您的 Python 打包工作流程,并创建高质量、可重用的包。